
#include "i386/system/segment.h"
#include "i386/pm_flags.h"
#include "i386/system/pm.h"
#include "i386/system/io.h"
#include "i386/system/reg.h"
#include "i386/system/screen.h"
#include "common/code16gcc.h"

static void realmode_switch_hook(void) {
  char msg[] = "prepare to enter protected model.";
  puts16(msg);
}

static void mask_all_interruptes(void) {
  outb(0xff, 0xa1);
  io_delay();
  outb(0xfb, 0x21);
  io_delay();
}

struct gdt_ptr {
  u16 len;
  u32 ptr;
} __attribute__((packed));

static const u64 boot_gdt[] __attribute__((aligned(16))) = {
  /* CS: code, read/exec, 4GB, base 0 */
  [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),
  /* DS: data, read/write, 4GB, base 0 */
  [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),
  
  /* tss: 32-bit TSS 104bytes, base 4096*/
  [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),
};

static struct gdt_ptr boot_gdtptr;

static void setup_gdt(void) {
  boot_gdtptr.len = sizeof(boot_gdt) - 1;
  boot_gdtptr.ptr = (u32)&boot_gdt + (ds() << 4);
  asm volatile("lgdtl %0" : : "m"(boot_gdtptr));
}

static void setup_idt(void) {
  static const struct gdt_ptr null_idt = {0, 0};
  asm volatile(
      "lidtl %0" : : "m"(null_idt)
               );
}

void go_to_protected_mode(void) {
  realmode_switch_hook();
  mask_all_interruptes();
  
  setup_idt();
  setup_gdt();

  /* jump into protected model */
  protected_mode_jump();
}
